//---------------------------------------------------------------------
// File: XmlValidationStep.cs
// 
// Summary: 
//
//---------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//---------------------------------------------------------------------

namespace Microsoft.Services.BizTalkApplicationFramework.BizUnit
{
	using System;
	using System.IO;
	using System.Xml;
	using System.Xml.Schema;

	/// <summary>
	/// The XmlValidationStep validates an Xml document, it may validate against a given schema, and also evaluate XPath queries.
	/// </summary>
	/// 
	/// <remarks>
	/// The following shows an example of the Xml representation of this test step.
	/// 
	/// <code escaped="true">
	///	<ValidationStep assemblyPath="" typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.XmlValidationStep">
	///		<XmlSchemaPath>.\TestData\PurchaseOrder.xsd</XmlSchemaPath>
	///		<XmlSchemaNameSpace>urn:bookstore-schema</XmlSchemaNameSpace>
	///		<XPathList>
	///			<XPathValidation query="/*[local-name()='PurchaseOrder' and namespace-uri()='http://SendMail.PurchaseOrder']/*[local-name()='PONumber' and namespace-uri()='']">PONumber_0</XPathValidation>
	///		</XPathList>
	///	</ValidationStep>
	///	</code>
	///	
	///	<list type="table">
	///		<listheader>
	///			<term>Tag</term>
	///			<description>Description</description>
	///		</listheader>
	///		<item>
	///			<term>XmlSchemaPath</term>
	///			<description>The XSD schema to use to validate the XML data <para>(optional)</para></description>
	///		</item>
	///		<item>
	///			<term>XmlSchemaNameSpace</term>
	///			<description>The XSD schema namespace to validate the XML data against <para>(optional)</para></description>
	///		</item>
	///		<item>
	///			<term>XPathList/XPathValidation</term>
	///			<description>XPath expression to evaluate against the XML document is defined in the attribute query, the expected result is defined in the element <para>(optional)(repeating)</para></description>
	///		</item>
	///	</list>
	///	</remarks>	

	public class XmlValidationStep : IValidationStep
	{
		/// <summary>
		/// IValidationStep.ExecuteValidation() implementation
		/// </summary>
		/// <param name='data'>The stream cintaining the data to be validated.</param>
		/// <param name='validatorConfig'>The Xml fragment containing the configuration for the test step</param>
		/// <param name='context'>The context for the test, this holds state that is passed beteen tests</param>
		public void ExecuteValidation(Stream data, XmlNode validatorConfig, Context context)
		{
			string xmlSchemaPath = context.ReadConfigAsString( validatorConfig, "XmlSchemaPath", true );
			string ns = context.ReadConfigAsString( validatorConfig, "XmlSchemaNameSpace", true );
			XmlNodeList xpaths = validatorConfig.SelectNodes( "XPathList/*" );

			XmlDocument doc = new XmlDocument();
			XmlTextReader trData = new XmlTextReader( data );
			XmlValidatingReader vr = new XmlValidatingReader(trData);

			// If schema was specified use it to vaidate against...
			if ( null != xmlSchemaPath )
			{
				MemoryStream xsdSchema = StreamHelper.LoadFileToStream(xmlSchemaPath);
				XmlTextReader trSchema = new XmlTextReader( xsdSchema );
				XmlSchemaCollection xsc = new XmlSchemaCollection();

				if ( null != ns )
				{
					xsc.Add( ns, trSchema );
					vr.Schemas.Add(xsc);
				}

				doc.Load(vr);
			}

			data.Seek(0, SeekOrigin.Begin);
			doc.Load(data);

			foreach( XmlNode xpath in xpaths)
			{
				string xpathExp = xpath.SelectSingleNode("@query").Value;
				XmlNode valNode = xpath.SelectSingleNode(".");

				string nodeValue = valNode.InnerText;

				context.LogInfo("XmlValidationStep evaluting XPath {0} equals \"{1}\"", xpathExp, nodeValue );

				XmlNode checkNode = doc.SelectSingleNode( xpathExp );

				if ( 0 != nodeValue.CompareTo( checkNode.InnerText ) )
				{
					throw new ApplicationException(string.Format( "XmlValidationStep failed, compare {0} != {1}, xpath query used: {2}", nodeValue, checkNode.InnerText, xpathExp ) );
				}
			}
        }
	}
}
